package com.scud.system.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.scud.common.constants.RedisConstants;
import com.scud.common.error.ErrorConstants;
import com.scud.common.exception.ScudException;
import com.scud.common.regex.RegexPatterns;
import com.scud.common.utils.JwtUtil;
import com.scud.model.system.dto.LoginDto;
import com.scud.model.system.pojo.Permission;
import com.scud.model.system.dto.RegisterDto;
import com.scud.model.system.pojo.User;
import com.scud.model.system.vo.MenuVo;
import com.scud.system.mapper.PermissionMapper;
import com.scud.model.system.pojo.UserRole;
import com.scud.system.mapper.UserMapper;
import com.scud.system.mapper.UserRoleMapper;
import com.scud.system.model.LoginUser;
import com.scud.system.properties.JwtProperties;
import com.scud.system.service.IndexService;
import com.scud.system.utils.SecurityUtil;
import com.scud.system.utils.TreeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author vermouth
 */
@Service
@Slf4j
public class IndexServiceImpl implements IndexService {

    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 发送验证码功能
     * @param phone :
     * @return Result
     */
    @Override
    public String sendCode(String phone) {
        //校验手机号
        if (StrUtil.isBlank(phone) || !phone.matches(RegexPatterns.PHONE_REGEX)) {
            throw new ScudException(ErrorConstants.PHONE_ILL_LEGAL);
        }
        //生成6位验证码
        String code = RandomUtil.randomNumbers(6);
        //保存在redis当中，并设置过期时间
        stringRedisTemplate.opsForValue().set(RedisConstants.CODE + phone, code,
                RedisConstants.CODE_TIME_OUT, TimeUnit.MINUTES);
        //todo 调用第三方接口发送验证码，这里受条件限制，不实现了

        //打印日志，返回
        log.info("生成的验证码为：{}", code);
        return code;
    }


    /**
     * 用户登录
     * @param loginDto
     * @return
     */
    @Override
    public String login(LoginDto loginDto) {
        String json = JSON.toJSONString(loginDto);
        //调用authenticationManager.authenticate进行认证
        Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(json, ""));
        //生成jwt令牌
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Map<String, Object> claims = new HashMap<>();
        claims.put("user_id", loginUser.getUser().getId());
        String jwt = JwtUtil.
                createJWT(jwtProperties.getSecretKey(), jwtProperties.getTtl(), claims);
        //4.存入redis
//        stringRedisTemplate.opsForValue().set("user::id" + user.getId(), jwt);
        stringRedisTemplate.opsForValue().set(RedisConstants.USER + loginUser.getUser().getId(), JSON.toJSONString(loginUser.getUser()),
                30, TimeUnit.MINUTES);
        return jwt;
    }

    /**
     * 获取用户信息
     * @param token
     * @return
     */
    @Override
    public User getUserinfo(String token) {
        //键不存在
//        if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(RedisConstants.USER + token))) {
//            throw new ScudException(ErrorConstants.TOKEN_INVALID);
//        }
//        User user1 = SecurityUtil.getUser();
//        System.out.println(user1);
//        String userJson = stringRedisTemplate.opsForValue().get(RedisConstants.USER + token);
//        User user = JSONObject.parseObject(userJson, User.class);
//        if (user != null) {
//            user.setPassword(null);
//        }
        //获取用户id
        Long userId = SecurityUtil.getUser().getId();
        User user;
        //判断redis当中是否存有用户数据
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisConstants.USER + userId))) {
            String userJson = stringRedisTemplate.opsForValue().get(RedisConstants.USER + userId);
            if (userJson!=null &&!userJson.equals("")  ) {
                user = JSONObject.parseObject(userJson, User.class);
                return user;
            }
        }
        //redis中没有用户数据，查询数据库
        user = userMapper.selectUserById(userId);
        //缓存在redis当中
        stringRedisTemplate.opsForValue().set(RedisConstants.USER + userId,JSON.toJSONString(user));
        return user;
    }

    /**
     * 管理端用户登录
     * @param loginDto:
     * @return Result<String>
     */
    @Override
    public String managerLogin(LoginDto loginDto) {
        //使用Security进行身份认证
        String json = JSON.toJSONString(loginDto);
        Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(json, ""));
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        //生成JWT令牌
        Map<String, Object> claims = new HashMap<>();
        log.info("authorities is {}", loginUser.getAuthorities());
        claims.put("permissions", loginUser.getAuthorities());
        claims.put("user_id", loginUser.getUser().getId());
        String jwt = JwtUtil.createJWT(jwtProperties.getSecretKey(), jwtProperties.getTtl(), claims);
        //存入redis
        stringRedisTemplate.opsForValue().set(RedisConstants.MANAGE_USER + loginUser.getUser().getId(),
                JSON.toJSONString(loginUser.getUser()), RedisConstants.USER_TIME_OUT, TimeUnit.MINUTES);
        return jwt;
    }

    @Override
    public User getManageUserinfo() {
        User user = SecurityUtil.getUser();
        log.info("user is : {}", user);
        if (user == null) {
            throw new ScudException(ErrorConstants.USER_ILL_LEGAL);
        }
        //从redis中获取用户信息
        String json = stringRedisTemplate.opsForValue().get(RedisConstants.MANAGE_USER + user.getId());
        User res = JSON.parseObject(json, User.class);
        res.setPassword(null);
        return res;
    }

    @Override
    @Transactional
    public void register(RegisterDto dto) {
        String phone = dto.getPhone();
        //封装user对象
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        //对密码进行加密
        user.setPassword(passwordEncoder.encode(dto.getPassword()));
        Long userId;
        //判断用户是否存在
        User dbUser = userMapper.getUserByPhone(phone);
        if (dbUser == null) {
            //用户不存在，插入用户数据
            userMapper.insertUser(user);
            userId = userMapper.getUserByPhone(phone).getId();
        } else {
            //用户存在，更改用户数据
            userId = dbUser.getId();
            userMapper.updateByPhone(user);
        }
        //出入用户角色数据
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(1);
        userRoleMapper.insert(userRole);
    }

    /**
     * 获取当前用户可操作菜单
     * @return
     */
    @Override
    public List<MenuVo> getCurrMenus() {
        //获取当前用户id
        User user = SecurityUtil.getUser();
        //获取所有权限
        List<Permission> permissions = permissionMapper.selectByUserId(user.getId());
        //去掉具体到后端接口的权限
        permissions.removeIf(permission -> StrUtil.isNotBlank(permission.getValue()));
        //构建树形结构
        List<Permission> tree = TreeUtil.buildTree(permissions);
        //转换返回格式
        return convert(tree);
    }

    /**
     * 用于转换返回结果
     * @return
     */
    private List<MenuVo> convert(List<Permission> tree) {
        List<MenuVo> menuVos = new LinkedList<>();
        for (Permission p : tree) {
            MenuVo menuVo = new MenuVo();
            menuVo.setTitle(p.getName());
            menuVo.setName(p.getComponent());
            List<Permission> children = p.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                menuVo.setChildren(convert(children));
            }
            menuVos.add(menuVo);
        }
        return menuVos;
    }
}
